<?php

namespace Xin\ThinkPHP\Model;

use think\db\Query;

/**
 * @mixin \think\Model
 * @method self simple(array|callable $appends = [])
 */
trait Modelable
{
    use HasSearch, HasSetValue;

    /**
     * Append attributes to query when building a query.
     *
     * @param array $append
     * @param bool $merge
     * @return $this
     */
    public function append(array $append = [], bool $merge = true)
    {
        $this->append = array_unique(
            array_merge($this->append, $append)
        );

        return $this;
    }

    /**
     * Set the accessors to append to model arrays.
     *
     * @param array $appends
     * @return $this
     */
    public function setAppends(array $appends)
    {
        $this->append = $appends;

        return $this;
    }

    /**
     * Remove the accessors to append to model arrays.
     *
     * @param array $appends
     * @return $this
     */
    public function removeAppends(array $appends)
    {
        foreach ($appends as $append) {
            if (($index = array_search($append, $this->append, true)) !== false) {
                unset($this->append[$index]);
            }
        }

        return $this;
    }

    /**
     * Return whether the accessor attribute has been appended.
     *
     * @param string $attribute
     * @return bool
     */
    public function hasAppended($attribute)
    {
        return in_array($attribute, $this->append, true);
    }

    /**
     * Get the hidden attributes for the model.
     *
     * @return array
     */
    public function getHidden()
    {
        return $this->hidden;
    }

    /**
     * Set the hidden attributes for the model.
     *
     * @param array $hidden
     * @return $this
     */
    public function setHidden(array $hidden)
    {
        $this->hidden = $hidden;

        return $this;
    }

    /**
     * Make the given, typically visible, attributes hidden.
     *
     * @param array $attributes
     * @return $this
     */
    public function makeHidden(array $attributes)
    {
        $this->hidden = array_merge(
            $this->hidden, $attributes
        );

        return $this;
    }

    /**
     * Make the given, typically visible, attributes hidden if the given truth test passes.
     *
     * @param bool|Closure $condition
     * @param array|string|null $attributes
     * @return $this
     */
    public function makeHiddenIf($condition, $attributes)
    {
        return value($condition, $this) ? $this->makeHidden($attributes) : $this;
    }

    /**
     * Get the visible attributes for the model.
     *
     * @return array
     */
    public function getVisible()
    {
        return $this->visible;
    }

    /**
     * Set the visible attributes for the model.
     *
     * @param array $visible
     * @return $this
     */
    public function setVisible(array $visible)
    {
        $this->visible = $visible;

        return $this;
    }

    /**
     * Make the given, typically hidden, attributes visible.
     *
     * @param array $attributes
     * @return $this
     */
    public function makeVisible(array $attributes)
    {
        $this->hidden = array_diff($this->hidden, $attributes);

        if (!empty($this->visible)) {
            $this->visible = array_merge($this->visible, $attributes);
        }

        return $this;
    }

    /**
     * Make the given, typically hidden, attributes visible if the given truth test passes.
     *
     * @param bool|Closure $condition
     * @param array $attributes
     * @return $this
     */
    public function makeVisibleIf($condition, array $attributes)
    {
        return value($condition, $this) ? $this->makeVisible($attributes) : $this;
    }

    /**
     * 简单数据查询作用域
     * @param \think\db\Query $query
     * @param array|callable $appends
     */
    public function scopeSimple(Query $query, $appends = [])
    {
        if (is_callable($appends)) {
            $fields = $appends(static::getSimpleFields(), $query, $this);
        } else {
            $fields = array_merge(static::getSimpleFields(), $appends);
        }

        $query->field($fields);
    }

    /**
     * 获取基础查询对象
     *
     * @param mixed $query
     * @param array $options
     * @return \think\db\Query|\think\Model
     */
    public static function simpleQuery($query = null, $options = [])
    {
        $fields = static::getSimpleFields();
        if (isset($options['field'])) {
            if (is_callable($options['field'])) {
                $fields = $options['field']($fields);
            } else {
                $fields = $options['field'];
            }
            unset($options['field']);
        }

        $model = new static;
        $newQuery = $model->field($fields);

        if ($query) {
            $newQuery->where($query);
        }

        return static::applyOptions($newQuery, $options);
    }

    /**
     * 获取列表要查询的字段列表，一般用于接口列表查询
     *
     * @return array
     */
    public static function getSimpleFields()
    {
        return [];
    }

    /**
     * 获取要公开的字段列表，一般用于管理查询数据
     * @return string[]
     */
    public static function getPublicFields()
    {
        return static::getSimpleFields();
    }

    /**
     * 应用 options
     *
     * @param mixed $baseQuery
     * @param array $options
     * @return \think\Model|\think\db\Query
     */
    public static function applyOptions($baseQuery, $options = null)
    {
        if ($options === null) {
            return $baseQuery;
        }

        if (is_callable($options)) {
            return $options($baseQuery);
        }

        foreach ($options as $method => $option) {
            if (method_exists($baseQuery, $method)) {
                if (is_array($option) && in_array($method, ['limit', 'page'])) {
                    $baseQuery->$method(...$option);
                } else {
                    $baseQuery->$method($option);
                }
            }
        }

        return $baseQuery;
    }
}
